<?php

namespace Service\SearchService\Index;
use Service\SearchService\BaseElasticSearchClientService;
use Service\Code\Normal;
use Service\SearchService\SearchService;

/**
 * B端商品搜索
 * @author chenshubo
 */
class ChannelCommodityIndex extends BaseElasticSearchClientService
{
    /**
     * 索引名字
     * @var string
     */
    public $index = 'channel_commodity';
    
    /**
     * 商品索引结构
     * @var array
     */
    public $mapping_property = [
        'ID' => [
            'type'  => 'keyword',
        ],
        'GoodsID' => [//排序使用
            'type'  => 'float',
        ],
        'ClassID' => [
            'type'  => 'keyword',
        ],
        'ChannelID' => [
            'type'  => 'keyword',
        ],
        'GoodsName' => [
            'type'      => 'text',
            'analyzer'  => 'ik_smart',
            'search_analyzer'   => 'ik_smart',
            'fields'    => [
                "keyword"=> [
                    "type"              => "text",
                    "analyzer"          => "ik_smart",
                    "search_analyzer"   => "ik_smart",
                    "index"             => true,
                    "store"             => true
                ],
                "suggest"=> [
                    "type"                          => "completion",
                    "analyzer"                      => "simple",
                    "preserve_separators"           => true,
                    "preserve_position_increments"  => true,
                    "max_input_length"              => 50
                ]
            ]
        ],
        'BrandName' => [
            'type'      => 'text',
            'analyzer'  => 'ik_smart',
            'search_analyzer'   => 'ik_smart',
            'fields'    => [
                "keyword"=> [
                    "type"              => "text",
                    "analyzer"          => "ik_smart",
                    "search_analyzer"   => "ik_smart",
                    "index"             => true,
                    "store"             => true
                ],
                "suggest"=> [
                    "type"                          => "completion",
                    "analyzer"                      => "simple",
                    "preserve_separators"           => true,
                    "preserve_position_increments"  => true,
                    "max_input_length"              => 50
                ]
            ]
        ],
    ];
    
    /**
     * 全量删除商品文档数据
     * @param array $body
     */
    public function deleteAllMultiDoc(array $param)
    {
        $cid = $param['datas'];
        $param = [
            'index' => $this->index,
            'body'  => [
                'script'=> ['source'=>'ctx._source.IsActive=false']
            ]
        ];
        $param['body']['query']['bool']['must'][] = ['match'=>['ChannelID'=>$cid]];
        $param['body']['query']['bool']['must'][] = ['match'=>['IsTrade'=>false]];
        $return =  $this->updateByQuery($param);
        if( empty($return['task']) ) {
            return Normal::returnCode(Normal::FAIL, "批量删除索引失败",$return);
        }
        return Normal::returnCode(Normal::SUC, "批量修改索引任务已经提交异步任务执行,请耐心等待处理数据结果!",$return);
    }

    /**
     * 批量处理新增/更新文档
     * @param array $param
     * @return int[]|string[]|array[]
     */
    public function addMultiDoc(array $param)
    {
        $DataList = $param['datas'];
        if (empty($DataList)) {
            return Normal::returnCode(Normal::SUC, "数据为空，不需要消费");
        }
        $body = [];
        foreach ($DataList as $item) 
        {
            $SkuID = $item['id'];
            $body[] = [
                'index' => [
                    '_index' => $this->index,
                    '_id' => $SkuID
                ]
            ];
            $body[] = [
                'ID'                => $item['id'],
                'GoodsID'           => $item['goodsId'],
                'GoodsName'         => $item['name'],
                'BrandName'         => $item['brandName'],
                'ClassID'           => $item['classifyID'],
                'ChannelID'         => $item['channelID'],
                'SuggestChannelID'  => $item['channelID'].'_'.(string)(int)$item['isChannelGoodsActive'],
                'IsActive'          => $item['isChannelGoodsActive'],
                'IsTrade'           => (bool)$item['isTrade']
            ];
        }
        $res = $this->bulk($body);
        if(!$res){
            return Normal::returnCode(Normal::FAIL, "批量创建索引失败");
        }
        return Normal::returnCode(Normal::SUC, "批量创建索引成功");
    }


    /**
     * 搜索商品品牌
     * @param array $param
     */
    public function searchBrand(array $param)
    {
        $query = $this->buildElasticSearch($param);
        if( empty($query) ) {
            $query['match_all'] =  new \stdClass();
        }
        $request = [
            'index'     => $this->index,
            'body'      => [
                'query'     => $query,
                'size'      => 0,
                'aggs'      => [
                    'unique_brands' => [
                        'terms' => [
                            'field' => 'BrandName',
                            'size'  =>  10000
                        ]
                    ]
                ]
            ],
        ];

        $return = $this->getAggsList($request);

        if( isset($return['aggregations']['unique_brands']['buckets']) ) {
            $hits = $return['aggregations']['unique_brands']['buckets'];
            $result = [];
            foreach ($hits as $item) {
                $result[] = $item['key'];
            }
            return Normal::returnCode(Normal::SUC,"搜索完成",$result);
        } else {
            return Normal::returnCode(Normal::FAIL,"搜索异常",$return);
        }
    }
    
    /**
     * 批量删除处理文档
     */
    public function deleteMultiDoc($param = [])
    {
        if (!isset($param['datas'])) {
            return Normal::returnCode(Normal::FAIL, "消息DataList不存在");
        }
        $DataList = $param['datas'];

        if (empty($DataList)) {
            return Normal::returnCode(Normal::SUC, "数据为空，不需要消费");
        }

        $param = [
            'index' => $this->index,
             'body' => [
                'query' => ['terms'=>['ID'=>$DataList]],
                'script'=> ['source'=>'ctx._source.IsActive=false'],
             ]
        ];
        $return = $this->updateByQuery($param);
        if( empty($return['task']) ) {
            return Normal::returnCode(Normal::FAIL, "批量修改索引失败",$return);
        }
        return Normal::returnCode(Normal::SUC, "批量修改索引任务已经提交异步任务执行,请耐心等待处理数据结果!",$return);
    }
    
    /**
     * 搜索商品
     * @return array
     */
    public function search($param)
    {
        $limit = (int)($param['limit'] ?? 10);
        $page  = (int)($param['page'] ?? 0);
        $query = $this->buildElasticSearch($param);
        $page  = $page - 1 < 0 ? 0 : $page -1;
        //价格排序
        $sortRule = [];        
        //ID正序
        $sortRule[]['_score']['order'] = 'desc';
        $sortRule[]['GoodsID']['order'] = 'asc';
        $request = [
            'index'     => $this->index,
            'body'      => [
                'query'     => $query,
                'size'      => $limit,
                'from'      => $page * $limit
            ],
        ];
        
        if( !empty($sortRule) ) {
           $request['body']['sort'] = $sortRule;
        }
        $return = $this->getAggsList($request);
        if( isset($return['hits']['hits']) ) {
            $hits = $return['hits']['hits'];
            $result = [];
            foreach ($hits as $item) {
                $result[] = $item['_source']['GoodsID'];
            }
            return Normal::returnCode(Normal::SUC,"搜索完成",$result);
        } else {
            return Normal::returnCode(Normal::FAIL,"搜索异常",$return);
        }
    }
    
    /**
     * 搜索建议搜索功能
     * @param int $mn_sid
     * @param string $keyword
     * @param int $limit
     */
    public function searchSuggest($param)
    {
        $cid        = $param['cid'];
        $keyword    = $param['keyword'];
        $limit      = $param['limit'] ?? 10;
        //搜索商品名称
        $suggest = [
            'commoditySuggest'  => [
                'prefix'        => $keyword,
                'completion'    => [
                    'contexts'    => [
                        'SuggestChannelID'  => [
                            [
                                'context' => "{$cid}_1"
                            ]
                        ]
                    ],
                    'fuzzy' => [
                        'fuzziness'     => 'AUTO',
                        'unicode_aware' => 'true',
                    ],
                    'skip_duplicates'   => true,
                    'field'             => 'GoodsName.suggest',
                    'size'              => $limit
                ]
            ]
        ];
        $request = [
            'index'     => $this->index,
            'body'      => [
                '_source'   => false,
                'suggest'   => $suggest,
            ],
        ];
        $result = $this->getAggsList($request);
        if( isset($result['suggest']['commoditySuggest']) )
        {
            $dataList = [];
            $commoditySuggestResult = current($result['suggest']['commoditySuggest'])['options'];
            if( empty($commoditySuggestResult) )
            {
                return Normal::returnCode(Normal::SUC,"未找到当前渠道ID{$cid}下搜索建议词汇【{$keyword}】数据",[
                    'word'  => []
                ]);
            }
            $dataList = array_column($commoditySuggestResult,'text');
            return Normal::returnCode(Normal::SUC,"查询数据成功",[
                'word'  => $dataList
            ]);
        }
        
        return Normal::returnCode(Normal::FAIL,"查询数据异常",$result);
    }
    
    /**
     * 构建搜索参数
     */
    public function buildElasticSearch( $param = [] )
    {
       //搜索商品名称
       $body = [];
       $body['bool']['filter'][] = ['term'=>['ChannelID'=>$param['cid']]];
       $body['bool']['filter'][] = ['term'=>['IsActive'=>true]];
       if( !empty($param['keyword']) )
       {
           $keyword = SearchService::instance()->getKeyword($param['keyword']);
           $shoulds_where[] = [
               'match' => [
                   'GoodsName' => [
                       'analyzer'  => 'standard',
                       'query'     => $keyword,
                       'operator'  => 'and',
                       'boost'     => 2.5
                   ]
               ]
           ];
           $shoulds_where[] = [
               'multi_match' => [
                   'analyzer'  => 'standard',
                   'fields'    => ['GoodsName'],
                   'query'     => $keyword,
                   'boost'     => 2.0
               ]
           ];
           $shoulds_where[] = [
               'wildcard' => [
                   'GoodsName' => [
                       'value'     => "*{$keyword}*",
                       'boost'     => 1.5
                   ]
               ]
           ];
           $shoulds_where[] = [
               'fuzzy' => [
                   'GoodsName' => [
                       'value'     => $keyword,
                       'boost'     => 1.0
                   ]
               ]
           ];
           $shoulds_where[] = [
               'match_phrase' => [
                   'GoodsName' => $keyword
               ]
           ];
           $body['bool']['must'][] = ['bool'=>['should'=>$shoulds_where]];   
       }
       
       //搜素品牌名称
       if( !empty($param['brand_name']) )
       {
           $keyword = $param['brand_name'];
//           $body['bool']['must'][] = ['match'=>['BrandName'=>[
//               'query'     => $keyword,
//               'operator'  => 'and'
//           ]]];
           $body['bool']['must'][] = ['term'=>['BrandName'=>$keyword]];

       }
       
       //搜索分类ID
       if( !empty($param['class_id']) )
       {
           $class_ids = $param['class_id'];
           $class_ids = array_filter(array_unique(\explode(",",$class_ids)));
           $body['bool']['must'][] = ['terms'=>['ClassID'=>$class_ids]];
       }
       
       //如果没有任何搜索条件
       if( empty($body) ) {
           $body['match_all'] = new \stdClass();
       }
       
       return $body;
    }
    
    /**
     * 获取搜索分组对应品牌，分类、商品类型数据
     * @param array $param
     */
    public function getSearchClassAndBrands( $param = [] )
    {
        $aggs = [];
        $aggs['f_class_id']['terms']['field']   = 'FirstClassificationID';
        $aggs['s_class_id']['terms']['field']   = 'SecondClassificationID';
        $aggs['t_class_id']['terms']['field']   = 'ThirdClassificationID';
        $aggs['goods_type']['terms']['field']   = 'CommodityType';
        $aggs['brand_id']['terms']['field']     = 'BrandID';
        $search = $this->buildElasticSearch($param);
        $body = [
            'query' => $search,
            'aggs' => $aggs,
        ];
        $request = [
            'index' => $this->index,
            'body'  => $body,
            'size'  => 0,
        ];
        return $this->getAggsList($request);
    }
    
    /**
     * 菜单分类树
     */
    public function getClassList()
    {
        $body = [];
        $body['index'] = $this->index;
        $param['size']  = 0;
        //将size设置为全量值，来解决精度问题  (-2147483648 - 2147483647)
        //问题链接：https://blog.csdn.net/weixin_48726650/article/details/107139023
        $maxSize = 2147483647;
        // $param['aggs']['f_class_id']['terms']['field'] = 'FirstClassificationID';
        $param['aggs']['f_class_id']['terms'] = [
            'field' => 'FirstClassificationID',
            'size'  => $maxSize
        ];
        // $param['aggs']['s_class_id']['terms']['field'] = 'SecondClassificationID';
        $param['aggs']['s_class_id']['terms'] = [
            'field' => 'SecondClassificationID',
            'size'  => $maxSize
        ];
        // $param['aggs']['t_class_id']['terms']['field'] = 'ThirdClassificationID';
        $param['aggs']['t_class_id']['terms'] = [
            'field' => 'ThirdClassificationID',
            'size'  => $maxSize
        ];
        $body['body'] = $param;
        return $this->getAggsList($body);
    }
    
    /**
     * 创建文档索引
     */
    public function putMapping( $param = [] )
    {
        $isExists = $this->existsIndices();
        if( !$isExists ) {
            return $this->createMapping();
        } else {
            return Normal::returnCode(Normal::SUC,"索引已存在",$this->getMapping());
        }
    }
}